/*
 * Copyright (C) 2017-2019 Alibaba Group Holding Limited
 */

/******************************************************************************
 * @file     camera_ov2732.c
 * @brief    CSI Source File for camera Driver
 * @version  V2.0
 * @date     10. Nov 2019
 ******************************************************************************/

/* LOG_LEVEL: 0: Err; 1: Err&Warn; 2: Err&Warn&Info; 3: Err&Warn&Info&Debug */
#define LOG_LEVEL 0
#include <syslog.h>

#include <stdio.h>
#include <string.h>
// #include <csi_config.h>
#include <soc.h>
#include <io.h>
#include <pin.h>
#include <drv_usi_iic.h>
#include <drv_camera.h>
#include "camera_ov2732.h"

#define ARRAY_SIZE(arr)     ( sizeof(arr) / sizeof((arr)[0]) )
#define ERR_CAMERA(errno) (CSI_DRV_ERRNO_CAMERA_BASE | errno)
#define CAMERA_NULL_PARAM_CHK(para) HANDLE_PARAM_CHK(para, ERR_CAMERA(DRV_ERROR_PARAMETER))

extern ov_priv_t camera_handle[CONFIG_CAMERA_NUM];
extern const struct regval_list sensor_oe_enable_regs[3];
extern const struct regval_list sensor_oe_disable_regs[3];

static void camera_iic_event_cb_fun(int32_t idx, iic_event_e event)
{
    if (event == IIC_EVENT_TRANSFER_DONE) {
        int i;
        for (i = 0; i < CONFIG_CAMERA_NUM; i++) {
            ov_priv_t *camera_priv = &(camera_handle[i]);
            if (camera_priv != NULL && camera_priv->init_flag &&
                camera_priv->commu_info.iic_idx == idx) {
                camera_priv->commu_info.cb_transfer_flag = 1;
                return;
            }
        }
        LOG_E("No camera match, idx=%d\n", idx);
    }
}

int32_t csi_camera_start(camera_handle_t handle, camera_config_t *config)
{
    int32_t ret;
    CAMERA_NULL_PARAM_CHK(handle);
    ov_priv_t *camera_priv = (ov_priv_t *)handle;

    if (config->height > OV2732_WINDOW_HEIGHT_MAX || config->width > OV2732_WINDOW_WIDTH_MAX) {
        LOG_E("height(%d) or width(%d) not supported\n", config->height, config->width);
        return -1;
    }

    if (config->format != CAMERA_DATA_FORMAT_RAW8 && config->format != CAMERA_DATA_FORMAT_RAW10) {
        LOG_E("format(%d) not supported\n", config->format);
        return -1;
    }

    LOG_D("CSI_SUBDEV_PWR_ON!\n");
    ret = ov_set_stby(&camera_priv->commu_info, CSI_STBY_OFF);
    if(ret < 0) {
        LOG_E("soft stby off failed!\n");
        return -1;
    }

    ret = ov_write_array(&camera_priv->commu_info,
                         (struct regval_list *)ov2732_1920x1080,
                         ARRAY_SIZE(ov2732_1920x1080));
    if(ret < 0) {
        LOG_E("ov_write_array() failed, ret=%d\n", ret);
        return -1;
    }
    return 0;
}

int32_t csi_camera_stop(camera_handle_t handle)
{
    int32_t ret;
    CAMERA_NULL_PARAM_CHK(handle);
    ov_priv_t *camera_priv = (ov_priv_t *)handle;

    LOG_D("CSI_SUBDEV_PWR_OFF!\n");
    ret = ov_write_array(&camera_priv->commu_info,
                         (struct regval_list *)sensor_oe_disable_regs,
                         ARRAY_SIZE(sensor_oe_disable_regs));
    if(ret < 0) {
        LOG_E("disable oe failed!\n");
        return -1;
    }
    //software standby on
    ret = ov_set_stby(&camera_priv->commu_info, CSI_STBY_ON);
    if(ret < 0) {
        LOG_E("soft stby failed!\n");
        return -1;
    }
    return 0;
}

camera_handle_t csi_camera_initialize(int32_t idx, camera_event_cb_t cb_event, void *user_data)
{
    if (idx < 0 || idx >= CONFIG_CAMERA_NUM) {
        LOG_E("CAMERA input id error.\n");
        return NULL;
    }

    ov_priv_t *camera_priv = &camera_handle[idx];
    if (camera_priv->init_flag == 1) {
        LOG_E("CAMERA has been initialized\n");
        return NULL;
    }

    int32_t ret;
    int32_t iic_idx = -1;
    if (idx == 0) {
        #ifdef EXAMPLE_CAMERA0_IIC_IDX
        iic_idx = EXAMPLE_CAMERA0_IIC_IDX;
        #endif
    } else if (idx == 1) {
        #ifdef EXAMPLE_CAMERA1_IIC_IDX
        iic_idx = EXAMPLE_CAMERA1_IIC_IDX;
        #endif
    } else if (idx == 2) {
        #ifdef EXAMPLE_CAMERA2_IIC_IDX
        iic_idx = EXAMPLE_CAMERA2_IIC_IDX;
        #endif
    }
    if (iic_idx < 0) {
        LOG_E("Camera idx(%d) not defined iic id\n", idx);
        return NULL;
    }

    iic_handle_t iic_handle = csi_iic_initialize(iic_idx, camera_iic_event_cb_fun);
    ret = csi_iic_config(iic_handle, IIC_MODE_MASTER, IIC_BUS_SPEED_STANDARD,
                         IIC_ADDRESS_7BIT, OV2732_IIC_SLAVE_ADDR);
    if (ret < 0) {
        LOG_E("CAMERA(%d) init fail\n", idx);
        return NULL;
    }

    camera_priv->base_info.pll_clock = 96000000;
    camera_priv->base_info.height = OV2732_WINDOW_HEIGHT_DEF;
    camera_priv->base_info.width = OV2732_WINDOW_WIDTH_DEF;
    camera_priv->base_info.chipid_high = OV2732_CHIPID_HIGH;
    camera_priv->base_info.chipid_low = OV2732_CHIPID_LOW;

    camera_priv->status.standby = 1;
    camera_priv->status.pwoer = 0;
    camera_priv->user_data = user_data;

    camera_priv->commu_info.iic_idx = iic_idx;
    camera_priv->commu_info.iic_handle = iic_handle;
    camera_priv->commu_info.slave_addr = OV2732_IIC_SLAVE_ADDR;
    camera_priv->commu_info.cb_transfer_flag = 0;
    camera_priv->init_flag = 1;
    LOG_I("Using iic-id=%d, slave_addr=0x%02x\n", iic_idx, OV2732_IIC_SLAVE_ADDR);

    /* Check the id of camera */
    ret = ov_check_id(&camera_priv->commu_info, OV2732_CHIPID_HIGH, OV2732_CHIPID_LOW);
    if (ret < 0) {
        LOG_E("CAMERA(%d) check id fail\n", idx);
        csi_camera_uninitialize((camera_handle_t)camera_priv);
        return NULL;
    }
    return (camera_handle_t)camera_priv;
}

int32_t csi_camera_uninitialize(camera_handle_t handle)
{
    CAMERA_NULL_PARAM_CHK(handle);
    ov_priv_t *camera_priv = (ov_priv_t *)handle;

    csi_iic_uninitialize(camera_priv->commu_info.iic_handle);
    memset(camera_priv, 0, sizeof(ov_priv_t));
    return 0;
}
